home *** CD-ROM | disk | FTP | other *** search
- /********************************************************************************
-
- NeoTextBox.c - a replacement for TextBox
-
- Written by Bryan K. Ressler (Beaker)
-
- ********************************************************************************/
- #include "NTBDemo.h"
- #include "NeoTextBox.h"
-
- /** STATIC PROTOTYPES **********************************************************/
- static unsigned short NTBLineHeight(unsigned char *theText, unsigned long textLen,
- Rect *wrapBox, short lhCode, short *startY);
- static void NTBDraw(StyledLineBreakCode breakCode, unsigned char *lineStart,
- long lineBytes, Rect *wrapBox, short align, short curY, short boxWidth);
-
- /********************************************************************************
- *
- * NTBLineHeight - figures line height
- *
- * Input: theText the entire text that was given to the NeoTextBox call
- * textLen the length in bytes of the text
- * lhCode the line height code that was passed to NeoTextBox
- * startY VAR - we return the starting vertical pen location here
- *
- * Output: returns the line height to use
- *
- ********************************************************************************/
- unsigned short NTBLineHeight(unsigned char *theText, unsigned long textLen,
- Rect *wrapBox, short lhCode, short *startY)
- {
- short asc,desc; /* Used in the OutlineMetrics calls */
- FontInfo fInfo; /* The old-style font information record */
- Point frac; /* The fraction for the TrueType calls */
- unsigned short lineHeight; /* The return value */
-
- GetFontInfo(&fInfo);
- if (lhCode < 0) {
-
- /*
- If the user has specified variable-height lines, we need to try
- to determine the tallest ascent in the given text. We can only
- really do this if the font is a TrueType font. Otherwise, we
- punt and use old-fashioned GetFontInfo numbers.
- */
-
- frac.h = frac.v = 1;
- if (gHasTrueType && IsOutline(frac, frac)) {
-
- /*
- At this point we know the current font is a TrueType font, so
- we do an OutlineMetrics call with our full text. It will put
- the tallest character ascent into asc, and the deepest descent
- into desc. Then we choose between whichever's most between
- the old-style ascent/descent and the numbers we get from
- the OutlineMetrics call.
- */
-
- OutlineMetrics((short)textLen, theText, frac, frac, &asc, &desc,
- nil, nil, nil);
- lineHeight = MAXOF(fInfo.ascent, asc) + MAXOF(fInfo. descent,-desc) +
- fInfo.leading;
- *startY = wrapBox->top + MAXOF(fInfo.ascent, asc);
- *startY += fInfo.leading;
-
- } else {
-
- /*
- At this point we know the current font isn't TrueType, so we
- just use the old way of calculating line height.
- */
-
- lineHeight = fInfo.ascent + fInfo.descent + fInfo.leading;
- *startY = wrapBox->top + fInfo.ascent + fInfo.leading;
- }
-
- } else if (lhCode == 0) {
-
- /*
- If the user has specified "default" line height, he just wants us
- to get the line height from the FontInfo record.
- */
-
- lineHeight = fInfo.ascent + fInfo.descent + fInfo.leading;
- *startY = wrapBox->top + fInfo.ascent + fInfo.leading;
-
- } else {
-
- /* If the user has provided a specific line height, we just trust
- them. We can't really generate too good of a starting vertical
- coordinate, but we munge one together anyway.
- */
-
- lineHeight = lhCode;
- *startY = wrapBox->top + lhCode + fInfo.leading;
-
- }
-
- return(lineHeight);
- }
-
- /********************************************************************************
- *
- * NTBDraw - draws a line with appropriate justification
- *
- * Input: breakCode the break code that was returned from StyledLineBreak
- * lineStart pointer to the beginning of the text for the current line
- * lineBytes the length in bytes of the the text for this line
- * wrapBox the box within which we're wrapping
- * align the text alignment as specified by the user
- * curY our current vertical pen coordinate
- * boxWidth the width of wrapBox (since NeoTextBox already calculated it)
- *
- * Output: none (draws on the screen)
- *
- ********************************************************************************/
- void NTBDraw(StyledLineBreakCode breakCode, unsigned char *lineStart,
- long lineBytes, Rect *wrapBox, short align, short curY, short boxWidth)
- {
- unsigned long blackLen; /* Length of non-white characters */
- short slop; /* Number of pixels of slop for full just */
-
- /*
- The first thing we do here is determine the length of the "black" part
- of the current line. This excludes spaces, carriage returns, and other
- white stuff depending on the language. How do we know what to elim-
- inate? We DON'T! So we ask our friend the Script Manager to do it
- for us. VisibleLength returns the number of bytes that we should use
- for pixel width calculations.
- */
-
- blackLen = VisibleLength(lineStart, lineBytes);
-
- if (align == ntbJustFull) {
-
- /*
- For full justification, we need to calculate the "slop" space on
- the line that's not filled up by the text. Then we move to the
- margin and get ready to draw BUT WAIT! If this is the last line of
- a paragraph, we need to draw it with whatever the system justifi-
- cation is. So if the break code indicates we're at the end of the
- text, or we can find a carriage return there ourselves, we just
- change the text alignment to the current default system text align-
- ment and fall through without drawing. If it's just another line
- within a paragraph, we use the handy Script Manager routine DrawJust
- to draw it justified. In languages like Arabic, full justification
- is performed differently than just spacing out the words. DrawJust
- handles cases like these correctly.
-
- Note that when we go looking for the carriage return at the end of
- the line, it's okay to simply index to the last byte of the string.
- This is because the carriage return character, 0x0d, is in the
- control-code range, which is defined to never be the low byte of a
- two byte character.
- */
-
- slop = boxWidth - TextWidth(lineStart, 0, blackLen);
- MoveTo(wrapBox->left, curY);
- if (breakCode == smBreakOverflow ||
- *(lineStart + (lineBytes - 1)) == kReturnChar)
- align = GetSysJust();
- else DrawJust(lineStart, blackLen, slop);
- }
-
- /*
- For the rest of the text alignments (left, center, and right), we just
- move the pen to the right place and draw the text with DrawText. Note
- that the alignments that could have come into the NeoTextBox call have
- been resoved down to one of the four constants teForceLeft, teJustRight
- teJustCenter, or ntbJustFull.
- */
-
- switch(align) {
- case teForceLeft:
- case teJustLeft:
- MoveTo(wrapBox->left, curY);
- break;
- case teJustRight:
- MoveTo(wrapBox->right - TextWidth(lineStart, 0, blackLen), curY);
- break;
- case teJustCenter:
- MoveTo(wrapBox->left + (boxWidth - TextWidth(lineStart, 0, blackLen)) / 2,
- curY);
- break;
- }
- if (align != ntbJustFull)
- DrawText(lineStart, 0, lineBytes);
- }
-
- /********************************************************************************
- *
- * NeoTextBox - word-wraps text inside a given box
- *
- * Input: theText the text we need to wrap
- * textLen the length in bytes of the text
- * wrapBox the box within which we're wrapping
- * align the text alignment
- * teForceLeft, teFlushLeft left justified
- * teJustCenter, teCenter center justified
- * teJustRight, teFlushRight right justified
- * ntbJustFull full justified
- * teJustLeft, teFlushDefault system justified
- * lhCode the line height code that was passed to NeoTextBox
- * < 0 variable - based on tallest character
- * 0 default - based on GetFontInfo
- * > 0 fixed - use lhCode as the line height
- * endY VAR - if non-nil, the vertical coord of the last line
- * lhUsed VAR - if non-nil, the line height used to draw the text
- *
- * Output: returns the number of line drawn total (even if they drew outside of
- * the boundries of wrapBox)
- *
- ********************************************************************************/
- short NeoTextBox(unsigned char *theText, unsigned long textLen, Rect *wrapBox,
- short align, short lhCode, short *endY, short *lhUsed)
- {
- RgnHandle oldClip; /* Saved clipping region */
- StyledLineBreakCode breakCode; /* Returned code from StyledLineBreak */
- Fixed fixedMax; /* boxWidth converted to fixed point */
- Fixed wrapWid; /* Width to wrap to */
- short boxWidth; /* Width of the wrapBox */
- long lineBytes; /* Number of bytes in one line */
- unsigned short lineHeight; /* Calculated line height */
- short curY; /* Current vertical pen location */
- unsigned short lineCount; /* Number of lines we've drawn */
- long textLeft; /* Pointer to remaining bytes of text */
- unsigned char *lineStart; /* Pointer to beginning of a line */
- unsigned char *textEnd; /* Pointer to the end of input text */
-
- /*
- First, we save the old clipping region and clip to wrapBox. Then, figure
- the width of wrapBox, and make a fixed point version of it. Also, resolve
- the text alignment teFlushDefault to be the default system text alignment.
- */
-
- GetClip((oldClip = NewRgn()));
- ClipRect(wrapBox);
- boxWidth = wrapBox->right - wrapBox->left;
- fixedMax = Long2Fix((long)boxWidth);
- if (align == teFlushDefault)
- align = GetSysJust();
-
- /*
- Now we call NTBLineHeight to calculate the appropriate line height. It
- also figures our starting vertical pen location in curY based on the
- line height and other metric parameters. Clear lineCount, set
- lineStart to point to the beginning of the text, calculate textEnd for
- comparison to know when we're done, and preset textLeft to be all the
- text.
- */
-
- lineHeight = NTBLineHeight(theText, textLen, wrapBox, lhCode, &curY);
- lineCount = 0;
- lineStart = theText;
- textEnd = theText + textLen;
- textLeft = textLen;
-
- /*
- This is the main wrap-and-draw loop. I bet you never thought wrapping
- text could be so easy...
- */
-
- do {
-
- /*
- Every line, we have to preset lineBytes to something non-zero.
- This tells StyledLineBreak that we're drawing the first format
- run on the line (of course, for us, there's only ONE format run
- total). Also preset wrapWid. StyledLineBreak will always modify
- lineBytes (to tell you how many bytes are on this line), and will
- modify wrapWid, so we have to reset them each line.
- */
-
- lineBytes = 1;
- wrapWid = fixedMax;
-
- breakCode = StyledLineBreak(lineStart, textLeft, 0, textLeft, 0,
- &wrapWid, &lineBytes);
-
- /*
- Now that the Script Manager has done all the really hard work for
- us, we draw the line. We already knew lineStart, StyledLineBreak
- gave us lineBytes, which we pass to NTBDraw. It'll handle the
- different text alignments itself.
- */
-
- NTBDraw(breakCode, lineStart, lineBytes, wrapBox, align, curY, boxWidth);
-
- /*
- Now we advance our vertical position down by the height of one
- line, advance lineStart by the number of bytes we just drew,
- calculate a new textLeft, and increment our line count.
- */
-
- curY += lineHeight;
- lineStart += lineBytes;
- textLeft -= lineBytes;
- lineCount++;
-
- } while (lineStart < textEnd);
-
- /*
- Well that was a job well done. Let's return some useful values, too.
- If the user gave pointers for endY and lhUsed, we stuff our ending
- vertical coordinate and the line height we calculated into those,
- respectively. These allow the guy to put something after the wrapped
- text (or at least know the right place to put it).
- */
-
- if (endY)
- *endY = curY - lineHeight;
- if (lhUsed)
- *lhUsed = lineHeight;
-
- /*
- Finally, restore the clipping region, dispose of the region, and
- return the TOTAL number of lines drawn (note that we didn't stop
- drawing when curY advanced past wrapBox->bottom. This way, the user
- could tell that the text overflowed wrapBox. If you want to know how
- many lines fit, divide wrapBox by lhUsed. This way, you get the best
- of both worlds.
- */
-
- SetClip(oldClip);
- DisposeRgn(oldClip);
-
- return(lineCount);
- }
-